home *** CD-ROM | disk | FTP | other *** search
/ Delphi 2 - Developers' Solutions / Delphi 2 Developers' Solutions.iso / dds / chap10 / howto05 / delphi10 / ccicnntp.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-12  |  36.5 KB  |  1,024 lines

  1. unit Ccicnntp;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  7.   Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, Menus, FileCtrl, CCWSock, CCICCInf,
  8.   CCICCPrf, IniFiles, Gauges, CCiccfrm;
  9. type
  10.   { Component to hold NNTP handling capabilities }
  11.   TNNTPComponent = class( TWinControl )
  12.   public
  13.     NNTPCommandInProgress ,
  14.     Connection_Established : Boolean;
  15.     Socket1 : TCCSocket;
  16.     constructor Create( AOwner : TComponent ); override;
  17.     destructor Destroy; override;
  18.     function EstablishConnection( PCRPointer : PConnectionsRecord ) : Boolean;
  19.     function Disconnect : Boolean;
  20.     function DoCStyleFormat(       TheText      : string;
  21.                              const TheArguments : array of const ) : String;
  22.     procedure UpdateGauge( BytesFinished , TotalToHandle : longint );
  23.     procedure AddProgressText( WhatText : String );
  24.     procedure ShowProgressText( WhatText : String );
  25.     procedure ShowProgressErrorText( WhatText : String );
  26.     function GetNNTPServerResponse( var ResponseString : String ) : integer;
  27.     procedure NNTPSocketsErrorOccurred( Sender     : TObject;
  28.                                      ErrorCode  : Integer;
  29.                                      TheMessage : String   );
  30.     function PerformNNTPCommand(
  31.                     TheCommand   : string;
  32.               const TheArguments : array of const ) : Integer;
  33.     function PerformBlindNNTPCommand( TheCommand   : string ) : Integer;
  34.     function PerformNNTPExtendedCommand(
  35.                     TheCommand   : string;
  36.               const TheArguments : array of const ) : Integer;
  37.     function GetNNTPServerExtendedResponse( ResponseString : PChar ) : integer;
  38.     function GetNextSDItem(     WorkingString : String;
  39.                             var TheIndex      : Integer ) : String;
  40.     function GetListOfAvailableNewsGroups : Boolean;
  41.     function PurgeReadSentArticleListings( TheNGRecord : PNewsGroupRecord ): Boolean;
  42.     procedure ParseNewsGroupListing(     TheListing : String;
  43.                                      var GroupName  : String;
  44.                                      var LowCurrent : Longint;
  45.                                      var HighCurrent : Longint;
  46.                                      var Postable    : Boolean  );
  47.     function SetCurrentNewsGroup( TheNGRecord : PNewsGroupRecord;
  48.                                   DoUpdate    : Boolean           ) : Boolean;
  49.     function CheckForNewNews( TheNGRecord : PNewsGroupRecord ) : Boolean;
  50.     function CheckAllNewNews : Boolean;
  51.     procedure ParseArticleListing(     TheListing       : String;
  52.                                    var TotalAvailable   : Longint;
  53.                                    var LowestAvailable  : Longint;
  54.                                    var HighestAvailable : Longint );
  55.     function GetArticleHeader( TheNumber     : Longint;
  56.                                TheReturnList : TStringList ) : Boolean;
  57.     function GetAllArticleHeaders( TheNGRecord : PNewsGroupRecord ) : Boolean;
  58.     function GetHeaderSubject( HList : TStringList ) : String;
  59.     function GetHeaderSender( HList : TStringList ) : String;
  60.   end;
  61.  
  62. implementation
  63.  
  64. { This function calls an extended response NNTP command routine }
  65. function TNNTPComponent.PerformNNTPExtendedCommand(
  66.                TheCommand   : string;
  67.          const TheArguments : array of const ) : Integer;
  68. var TheBuffer : string; { Text buffer }
  69. begin
  70.   { If command in progress send back -1 error }
  71.   if NNTPCommandInProgress then
  72.   begin
  73.     Result := -1;
  74.     exit;
  75.   end;
  76.   { Set status variable }
  77.   NNTPCommandInProgress := True;
  78.   { Set global error code }
  79.   GlobalErrorCode := 0;
  80.   { Format output string }
  81.   TheBuffer := Format( TheCommand , TheArguments );
  82.   { Preset failure code }
  83.   Result := TCPIP_STATUS_FATAL_ERROR;
  84.   { If invalid socket or no connection abort }
  85.   if ( Socket1.TheSocket = INVALID_SOCKET ) or not Connection_Established then
  86.    exit;
  87.   { Send the buffer plus EOL chars }
  88.   Socket1.StringData := TheBuffer + #13#10;
  89.   { if abort due to timeout or other error exit }
  90.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  91.   { Otherwise return preliminary code }
  92.   Result := TCPIP_STATUS_PRELIMINARY;
  93. end;
  94.  
  95. { This function gets an extended period-ended multiline response from the server }
  96. function TNNTPComponent.GetNNTPServerExtendedResponse( ResponseString : PChar ) : integer;
  97. var
  98.   { Assume ResponseString already allocated as 0..513 }
  99.   { Pointer to the response string }
  100.   TheBuffer ,
  101.   BufferPointer : array[0..255] of char;
  102.   HolderBuffer : array[0..513] of char;
  103.   { Character to check for response code }
  104.   ResponseChar   : char;
  105.   { Pointers into returned string }
  106.   TheIndex ,
  107.   TheLength     : integer;
  108.   { Control variable }
  109.   LeftoversInPan ,
  110.   Finished      : Boolean;
  111.   BufferString : String;
  112. begin
  113.   { Preset fatal error }
  114.   Result := TCPIP_STATUS_FATAL_ERROR;
  115.   { Start loop control }
  116.   LeftoversInPan := false;
  117.   Finished := false;
  118.   StrCopy( HolderBuffer , '' );
  119.   repeat
  120.     { Do a peek }
  121.     BufferString := Socket1.PeekData;
  122.     { If timeout or other error exit }
  123.     if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  124.     { Find end of line character }
  125.     TheIndex := Pos( #10 , BufferString );
  126.     if TheIndex = 0 then
  127.     begin
  128.       TheIndex := Pos( #13 , BufferString );
  129.       if TheIndex = 0 then
  130.       begin
  131.         TheIndex := Pos( #0 , BufferString );
  132.         if TheIndex = 0 then
  133.         begin
  134.           TheIndex := Length( BufferString );
  135.           LeftoversInPan := True;
  136.           StrPCopy( TheBuffer , BufferString );
  137.           StrCat( HolderBuffer , TheBuffer );
  138.           LeftoversOnTable := false;
  139.         end;
  140.       end;
  141.     end;
  142.     { If an end of line then process the line }
  143.     if TheIndex > 0 then
  144.     begin
  145.       { Get length of string }
  146.       TheLength := TheIndex;
  147.       { Receive actual data }
  148.       Socket1.CCSockReceive( Socket1.TheSocket   ,
  149.                              @BufferPointer[ 0 ] ,
  150.                              TheLength              );
  151.       { Abort if timeout or error }
  152.       if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  153.       { Put in the length byte }
  154.       BufferPointer[ TheLength ] := Chr( 0 );
  155.       if LeftOversOnTable then
  156.       begin
  157.         LeftOversOnTable := false;
  158.         StrCopy( ResponseString , HolderBuffer );
  159.         StrCat( ResponseString , BufferPointer );
  160.       end
  161.       else
  162.       begin
  163.         if not LeftoversInPan then StrCopy( ResponseString , BufferPointer );
  164.       end;
  165.       if LeftoversInPan then
  166.       begin
  167.         LeftoversInPan := false;
  168.         LeftoversOnTable := true;
  169.       end
  170.       else
  171.       begin
  172.         ResponseChar := ResponseString[ 0 ];
  173.         if (( ResponseChar = '.' ) and ( StrLen( ResponseString ) <= 3 )) then
  174.         begin
  175.           Finished := true;
  176.           Result := TCPIP_STATUS_COMPLETED;
  177.         end
  178.         else
  179.         begin
  180.           if ResponseChar = '.' then ResponseString[ 0 ] := ' ';
  181.           Finished := true;
  182.           Result := TCPIP_STATUS_PRELIMINARY;
  183.         end;
  184.       end;
  185.     end;
  186.   until ( Finished and ( not LeftoversOnTable ));
  187.   StrLCopy( ResponseString , ResponseString , StrLen( ResponseString ) - 2 );
  188. end;
  189.  
  190. { This function moves along a string from an index, getting the next }
  191. { string delimited item or last one on string.                       }
  192. function TNNTPComponent.GetNextSDItem(     WorkingString : String;
  193.                                        var TheIndex      : Integer ) : String;
  194. var HoldingString : String;
  195. begin
  196.   HoldingString := Copy( WorkingString , TheIndex + 1 , 255 );
  197.   TheIndex := Pos( ' ' , HoldingString );
  198.   if TheIndex = 0 then
  199.   begin
  200.     Result := HoldingString;
  201.   end
  202.   else
  203.   begin
  204.     HoldingString := Copy( HoldingString , 1 , TheIndex - 1 );
  205.     Result := HoldingString;
  206.   end;
  207. end;
  208.  
  209. { This is the first true "network" function; it sends a LIST command, eats }
  210. { a single 215 response and then grabs PChars of data from the server till }
  211. { It returns a period character. The returned line is sent to a NEWSGRP    }
  212. { file and a status update is send through.                                }
  213. function TNNTPComponent.GetListOfAvailableNewsGroups : Boolean;
  214. var TheReturnString : String;  { Internal string holder }
  215.     TheResult       : Integer; { Internal int holder    }
  216.     HoldPChar ,
  217.     TheHoldingPChar ,
  218.     TheReturnPChar  : PChar;
  219.     TheNGFile       : TextFile;
  220.     D1 , D2     : Longint;
  221.     D3          : Boolean;
  222.     GroupString : String;
  223.     TotalGroups : Longint;
  224. begin
  225.   Result := false;
  226.   TheReturnString :=
  227.    DoCStyleFormat( 'LIST' ,
  228.     [ nil ] );
  229.   { Put result in progress and status line }
  230.   AddProgressText( TheReturnString );
  231.   ShowProgressText( TheReturnString );
  232.   { Begin login sequence with user name }
  233.   TheResult := PerformNNTPCommand( 'LIST', [ nil ] );
  234.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  235.   begin
  236.     NNTPCommandInProgress := false;
  237.     Result := false;
  238.     exit;
  239.   end;
  240.   repeat
  241.     TheResult := GetNNTPServerResponse( TheReturnString );
  242.     { Put result in progress and status line }
  243.     AddProgressText( TheReturnString );
  244.     ShowProgressText( TheReturnString );
  245.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  246.   NNTPCommandInProgress := false;
  247.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  248.   begin
  249.     { Do clever C formatting trick }
  250.     TheReturnString :=
  251.      DoCStyleFormat( 'LIST Failed!' ,
  252.       [ nil ] );
  253.     { Put result in progress and status line }
  254.     AddProgressText( TheReturnString );
  255.     ShowProgressErrorText( TheReturnString );
  256.     { Signal error }
  257.     Result := False;
  258.     { leave }
  259.     exit;
  260.   end;
  261.   try
  262.     AssignFile( TheNGFile , NewsPath + '\NEWSGRP.TXT' );
  263.     Rewrite( TheNGFile );
  264.   except
  265.     Socket1.OutOfBand := 'ABOR'+#13#10;
  266.     repeat
  267.       TheResult := GetNNTPServerResponse( TheReturnString );
  268.       { Put result in progress and status line }
  269.       AddProgressText( TheReturnString );
  270.       ShowProgressText( TheReturnString );
  271.     until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  272.     Result := false;
  273.     exit;
  274.   end;
  275.   GetMem( TheReturnPChar , 514 );
  276.   HoldPChar := TheReturnPChar;
  277.   TotalGroups := 0;
  278.   CCICInfoDlg.ListBox1.Clear;
  279.   repeat
  280.     Application.ProcessMessages;
  281.     if GlobalAbortedFlag then exit;
  282.     Inc(TotalGroups );
  283.     TheResult := GetNNTPServerExtendedResponse( TheReturnPChar );
  284.     if StrLen( TheReturnPChar ) > 255 then
  285.     begin
  286.       Getmem( TheHoldingPChar , 255 );
  287.       while StrLen( TheReturnPChar ) > 255 do
  288.       begin
  289.         StrCopy( TheHoldingPChar , '' );
  290.         StrMove( TheHoldingPChar , TheReturnPChar , 255 );
  291.         TheReturnPChar := TheReturnPChar + 256;
  292.         TheReturnString := StrPas( TheHoldingPChar );
  293.         ParseNewsGroupListing( TheReturnString, GroupString, D1 , D2 , D3 );
  294.       end;
  295.       FreeMem( TheHoldingPChar , 255 );
  296.       Writeln( TheNGFile , GroupString );
  297.       CCICInfoDlg.ListBox1.Items.Add( GroupString );
  298.       CCINetCCForm.Panel1.Caption := GroupString +
  299.        '(' + IntToStr( TotalGroups ) + ')';
  300.     end
  301.     else
  302.     begin
  303.       TheReturnString := StrPas( TheReturnPChar );
  304.       ParseNewsGroupListing( TheReturnString, GroupString, D1 , D2 , D3 );
  305.       Writeln( TheNGFile , GroupString );
  306.       CCICInfoDlg.ListBox1.Items.Add( GroupString );
  307.       CCINetCCForm.Panel1.Caption := GroupString +
  308.        '(' + IntToStr( TotalGroups ) + ')';
  309.     end;
  310.   until (( GlobalAbortedFlag ) or ( TheResult = TCPIP_STATUS_COMPLETED ));
  311.   FreeMem( HoldPChar , 514 );
  312.   CloseFile( TheNGFile );
  313.   Result := true;
  314.   CCINetCCForm.Panel1.Caption := 'Finished LIST!';
  315. end;
  316.  
  317. { This method sets a news group and updates its internal data }
  318. function TNNTPComponent.CheckForNewNews( TheNGRecord : PNewsGroupRecord ) : Boolean;
  319. begin
  320.   { Gee, that was easy! }
  321.   Result := SetCurrentNewsGroup( TheNGRecord , true );
  322. end;
  323.  
  324. { This method takes all the data in the NewsRCList and if subscribed, CNN's it }
  325. function TNNTPComponent.CheckAllNewNews : Boolean;
  326. var Counter_1   : Integer;
  327.     TheNGRecord : PNewsGroupRecord;
  328. begin
  329.   Result := true;
  330.   for Counter_1 := 0 to TheNewsRCList.Count - 1 do
  331.   begin
  332.     TheNGRecord := PNewsGroupRecord( TheNewsRCList.Items[ Counter_1 ] );
  333.     if TheNGRecord^.GSubScribed then Result := CheckForNewNews( TheNGRecord );
  334.   end;
  335. end;
  336.  
  337. { This method splits up a listing and pulls out its component data }
  338. procedure TNNTPComponent.ParseNewsGroupListing(     TheListing : String;
  339.                                 var GroupName  : String;
  340.                                 var LowCurrent : Longint;
  341.                                 var HighCurrent : Longint;
  342.                                 var Postable    : Boolean  );
  343. var HoldingString ,
  344.     HoldingString2 : String;
  345.     WorkingIndex  : Integer;
  346. begin
  347.   WorkingIndex := Pos( ' ' , TheListing );
  348.   if WorkingIndex = 0 then
  349.   begin
  350.     GroupName := TheListing;
  351.     LowCurrent :=  -1;
  352.     HighCurrent := -1;
  353.     Postable := false;
  354.     exit;
  355.   end;
  356.   GroupName := Copy( TheListing , 1 , WorkingIndex - 1 );
  357.   HoldingString := Copy( TheListing , WorkingIndex + 1 , 255 );
  358.   WorkingIndex := Pos(  ' ' , HoldingString );
  359.   HoldingString2 := Copy( HoldingString , 1 , WorkingIndex - 1 );
  360.   LowCurrent := StrToInt( HoldingString2 );
  361.   HoldingString := Copy( HoldingString , WorkingIndex + 1 , 255 );
  362.   WorkingIndex := Pos(  ' ' , HoldingString );
  363.   HoldingString2 := Copy( HoldingString , 1 , WorkingIndex - 1 );
  364.   HighCurrent := StrToInt( HoldingString2 );
  365.   HoldingString := Copy( HoldingString , WorkingIndex + 1 , 255 );
  366.   if (( HoldingString[ 1 ] = 'y' ) or ( HoldingString[ 1 ] = 'Y' )) then
  367.    Postable := true else Postable := false;
  368. end;
  369.  
  370. { This is another "Network" command which sets the GROUP to the name of the }
  371. { imported record. The imported record is also updated to reflect current   }
  372. { available articles.                                                       }
  373. function TNNTPComponent.SetCurrentNewsGroup(
  374.           TheNGRecord : PNewsGroupRecord; DoUpdate : Boolean ) : Boolean;
  375. var TheReturnString : String;  { Internal string holder }
  376.     TheResult       : Integer; { Internal int holder    }
  377.     TAA , LAA , HAA : Longint;
  378. begin
  379.   TheReturnString :=
  380.    DoCStyleFormat( 'GROUP %s' ,
  381.     [ TheNGRecord^.GRealName ] );
  382.   { Put result in progress and status line }
  383.   AddProgressText( TheReturnString );
  384.   ShowProgressText( TheReturnString );
  385.   { Begin login sequence with user name }
  386.   TheResult := PerformNNTPCommand( 'GROUP %s',
  387.                                   [ TheNGRecord^.GRealName ] );
  388.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  389.   begin
  390.     NNTPCommandInProgress := false;
  391.     Result := false;
  392.     exit;
  393.   end;
  394.   repeat
  395.     TheResult := GetNNTPServerResponse( TheReturnString );
  396.     { Put result in progress and status line }
  397.     AddProgressText( TheReturnString );
  398.     ShowProgressText( TheReturnString );
  399.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  400.   NNTPCommandInProgress := false;
  401.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  402.   begin
  403.     { Do clever C formatting trick }
  404.     TheReturnString :=
  405.      DoCStyleFormat( 'GROUP %s Not Available!' ,
  406.       [ TheNGRecord^.GRealName ] );
  407.     { Put result in progress and status line }
  408.     AddProgressText( TheReturnString );
  409.     ShowProgressErrorText( TheReturnString );
  410.     { Signal error }
  411.     Result := False;
  412.     { leave }
  413.     exit;
  414.   end;
  415.   Result := True;
  416.   { Leave if only want to set group }
  417.   if not DoUpdate then exit;
  418.   { Split out the articles listing into its three numbers }
  419.   ParseArticleListing( TheReturnString , TAA , LAA , HAA );
  420.   { Work on the numbers to make sure display is consistent }
  421.   with TheNGRecord^ do
  422.   begin
  423.     { Set internal pointers }
  424.     GTotalAvailable := TAA;
  425.     GLowestAvailable := LAA;
  426.     GHighestAvailable := HAA;
  427.     if GLowest < GLowestAvailable then
  428.     begin { All stored articles have expired or there are none }
  429.       GTotalNew := GTotalAvailable;      { Total new is total available    }
  430.       GLowest := GLowestAvailable - 1;   { set low and high to below start }
  431.       GHighest := GLowestAvailable - 1; { until something is read }
  432.     end
  433.     else
  434.     begin { Some read articles haven't expired; assume all still good }
  435.       GTotalNew := GHighestAvailable - GHighest; { Total since last download }
  436.       if GTotalNew < 0 then GTotalNew := 0; { Just in case... }
  437.     end;
  438.   end;
  439. end;
  440.  
  441. { This method splits out the GROUP response line into TAA, LAA , HAA }
  442. procedure TNNTPComponent.ParseArticleListing(     TheListing       : String;
  443.                               var TotalAvailable   : Longint;
  444.                               var LowestAvailable  : Longint;
  445.                               var HighestAvailable : Longint );
  446. var WorkingString ,
  447.     WorkingString2 : String;
  448.     WorkingIndex   : Integer;
  449. begin
  450.   WorkingString := Copy( TheListing , 5, 255 );
  451.   WorkingIndex := Pos( ' ' , WorkingString );
  452.   WorkingString2 := Copy( WorkingString , 1 , WorkingIndex - 1 );
  453.   TotalAvailable := StrToInt( WorkingString2 );
  454.   WorkingString := Copy( WorkingString , WorkingIndex + 1 , 255 );
  455.   WorkingIndex := Pos( ' ' , WorkingString );
  456.   WorkingString2 := Copy( WorkingString , 1 , WorkingIndex - 1 );
  457.   LowestAvailable := StrToInt( WorkingString2 );
  458.   WorkingString := Copy( WorkingString , WorkingIndex + 1 , 255 );
  459.   WorkingIndex := Pos( ' ' , WorkingString );
  460.   WorkingString2 := Copy( WorkingString , 1 , WorkingIndex - 1 );
  461.   HighestAvailable := StrToInt( WorkingString2 );
  462. end;
  463.  
  464. { This method uses the HEAD command to get a complete article header }
  465. function TNNTPComponent.GetArticleHeader( TheNumber     : Longint;
  466.                           TheReturnList : TStringList ) : Boolean;
  467. var TheReturnString : String;  { Internal string holder }
  468.     TheResult       : Integer; { Internal int holder    }
  469.     HoldPChar ,
  470.     TheReturnPChar ,
  471.     TheHoldingPChar : PChar;
  472. begin
  473.   TheReturnString :=
  474.    DoCStyleFormat( 'HEAD %d' ,
  475.     [ TheNumber ] );
  476.   { Put result in progress and status line }
  477.   AddProgressText( TheReturnString );
  478.   ShowProgressText( TheReturnString );
  479.   { Begin login sequence with user name }
  480.   TheResult := PerformNNTPCommand( 'HEAD %d', [ TheNumber ] );
  481.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  482.   begin
  483.     NNTPCommandInProgress := false;
  484.     Result := false;
  485.     exit;
  486.   end;
  487.   repeat
  488.     TheResult := GetNNTPServerResponse( TheReturnString );
  489.     { Put result in progress and status line }
  490.     AddProgressText( TheReturnString );
  491.     ShowProgressText( TheReturnString );
  492.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  493.   NNTPCommandInProgress := false;
  494.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  495.   begin
  496.     { Do clever C formatting trick }
  497.     TheReturnString :=
  498.      DoCStyleFormat( 'Head %d Failed!' ,
  499.       [ TheNumber ] );
  500.     { Put result in progress and status line }
  501.     AddProgressText( TheReturnString );
  502.     ShowProgressErrorText( TheReturnString );
  503.     { Signal error }
  504.     Result := False;
  505.     { leave }
  506.     exit;
  507.   end;
  508.   GetMem( TheReturnPChar , 514 );
  509.   HoldPChar := TheReturnPchar;
  510.   TheReturnList.Clear;
  511.   repeat
  512.     TheResult := GetNNTPServerExtendedResponse( TheReturnPChar );
  513.     if StrLen( TheReturnPChar ) > 255 then
  514.     begin
  515.       Getmem( TheHoldingPChar , 255 );
  516.       while StrLen( TheReturnPChar ) > 255 do
  517.       begin
  518.         StrCopy( TheHoldingPChar , '' );
  519.         StrMove( TheHoldingPChar , TheReturnPChar , 255 );
  520.         TheReturnPChar := TheReturnPChar + 256;
  521.         TheReturnString := StrPas( TheHoldingPChar );
  522.         TheReturnList.Add( TheReturnString );
  523.       end;
  524.       StrCopy( TheHoldingPChar , '' );
  525.       StrMove( TheHoldingPChar , TheReturnPChar , StrLen( TheReturnPChar ));
  526.       TheReturnString := StrPas( TheHoldingPChar );
  527.       TheReturnString := '\' + TheReturnString;
  528.       TheReturnList.Add( TheReturnString );
  529.       FreeMem( TheHoldingPChar , 255 );
  530.     end
  531.     else
  532.     begin
  533.       TheReturnString := StrPas( TheReturnPChar );
  534.       TheReturnList.Add( TheReturnString );
  535.     end;
  536.   until (( GlobalAbortedFlag ) or ( TheResult = TCPIP_STATUS_COMPLETED ));
  537.   FreeMem( HoldPChar , 514 );
  538.   Result := true;
  539. end;
  540.  
  541. { This method parses a header stringlist and obtains the subject line }
  542. function TNNTPComponent.GetHeaderSubject( HList : TStringList ) : String;
  543. var Counter_1     : Integer;
  544.     Finished      : Boolean;
  545.     WorkingIndex  : Integer;
  546.     WorkingString : String;
  547. begin
  548.   Counter_1 := 0;
  549.   Finished := false;
  550.   WorkingString := '[No Subject]';
  551.   while (( not Finished ) and ( Counter_1 < HList.Count - 1 )) do
  552.   begin
  553.     WorkingIndex := Pos( 'SUBJECT:' , Uppercase( HList.Strings[ Counter_1 ] ));
  554.     if WorkingIndex > 0 then
  555.     begin
  556.       WorkingString := Copy( HList.Strings[ Counter_1 ] , 9 , 255 );
  557.       Finished := true;
  558.     end
  559.     else Counter_1 := Counter_1 + 1;
  560.   end;
  561.   Result := WorkingString;
  562. end;
  563.  
  564. { This method parses a header stringlist and obtains the sender's ID }
  565. function TNNTPComponent.GetHeaderSender( HList : TStringList ) : String;
  566. var Counter_1     : Integer;
  567.     Finished      : Boolean;
  568.     WorkingIndex  : Integer;
  569.     WorkingString : String;
  570. begin
  571.   Counter_1 := 0;
  572.   Finished := false;
  573.   WorkingString := '';
  574.   while (( not Finished ) and ( Counter_1 < HList.Count - 1 )) do
  575.   begin
  576.     WorkingIndex := Pos( 'FROM:' , Uppercase( HList.Strings[ Counter_1 ] ));
  577.     if WorkingIndex > 0 then
  578.     begin
  579.       WorkingString := Copy( HList.Strings[ Counter_1 ] , 6 , 255 );
  580.       Finished := true;
  581.     end
  582.     else Counter_1 := Counter_1 + 1;
  583.   end;
  584.   Result := WorkingString;
  585. end;
  586.  
  587.  
  588. { This method updates the available headers in the header file for a newsgroup }
  589. function TNNTPComponent.GetAllArticleHeaders( TheNGRecord : PNewsGroupRecord ) : Boolean;
  590. var TheNGARecord   : PNewsGroupArticleRecord;
  591.     Counter_1      : Integer;
  592.     TheHeaderList  : TStringList;
  593.     WorkingList    : TList;
  594.     WorkingCounter : Longint;
  595. begin
  596.   { Do this for ease of coding }
  597.   with TheNGRecord^ do
  598.   begin
  599.     { Get the current TList of article headers }
  600.     WorkingList := TList( GLTag );
  601.     { Set Group Command without updating }
  602.     if not SetCurrentNewsGroup( TheNGRecord , false ) then
  603.     begin
  604.       { Abort if can't get newsgroup }
  605.       Result := false;
  606.       exit;
  607.     end;
  608.     { create the stringlist for header info }
  609.     TheHeaderList := TStringList.Create;
  610.     { Determine how many to get from computed availability }
  611.     WorkingCounter := GHighestAvailable - GTotalNew + 1;
  612.     { Run up to total new articles }
  613.     for Counter_1 := 1 to GTotalNew do
  614.     begin
  615.       { Try to get the header }
  616.       if GetArticleHeader( WorkingCounter , TheHeaderList ) then
  617.       begin
  618.         { If succeed create new header record }
  619.         New( TheNGARecord );
  620.         with TheNGARecord^ do
  621.         begin
  622.           { Fill in all the fields with nominal or acquired data }
  623.           NGAGroupname   := GRealName;
  624.           NGASubject     := GetHeaderSubject( TheHeaderList );
  625.           NGANumber      := WorkingCounter;
  626.           NGADownloaded  := false;
  627.           NGASender      := GetHeaderSender( TheHeaderList );
  628.           NGARead        := false;
  629.           NGAPosted      := false;
  630.           NGAArtFileName := '';
  631.         end;
  632.         { Put record on list }
  633.         WorkingList.Add( TheNGARecord );
  634.       end;
  635.       { Either way increment the counter }
  636.       WorkingCounter := WorkingCounter + 1;
  637.     end;
  638.     { Update all the pointer numbers to indicate all article headers gotten }
  639.     GTotalUnreadArticles := GTotalUnreadArticles + GTotalAvailable;
  640.     GTotalArticles := GTotalArticles + GTotalAvailable;
  641.     GTotalAvailable := 0;
  642.     GTotalNew := 0;
  643.     GLowestAvailable := GHighestAvailable;
  644.     GLowest := GLowestAvailable;
  645.     GHighest := GLowestAvailable;
  646.     { Save off the pointer to the modified TList }
  647.     GLTag := Longint( WorkingList );
  648.     { Clean Up and leave }
  649.     Result := true;
  650.     TheHeaderList.Free;
  651.   end;
  652. end;
  653.  
  654. { This function deletes all read/sent articles and associated files }
  655. function TNNTPComponent.PurgeReadSentArticleListings(
  656.  TheNGRecord : PNewsGroupRecord ) : Boolean;
  657. var TheNGARecord   : PNewsGroupArticleRecord;
  658.     Counter_1      : Integer;
  659.     WorkingList    : TList;
  660.     Finished       : Boolean;
  661. begin
  662.   { Do this for ease of coding }
  663.   with TheNGRecord^ do
  664.   begin
  665.     { Get the current TList of article headers }
  666.     WorkingList := TList( GLTag );
  667.     { Run up to total new articles }
  668.     for Counter_1 := 0 to WorkingList.Count - 1 do
  669.     begin
  670.       TheNGARecord := PNewsGroupArticleRecord( WorkingList.Items[ Counter_1 ] );
  671.       if ( TheNGARecord^.NGAPosted or TheNGARecord^.NGARead ) then
  672.       begin
  673.         Dec( GTotalArticles );
  674.         if FileExists( NewsPath + '\' + TheNGARecord^.NGAArtFilename ) then
  675.          {DeleteFile( NewsPath + '\' + TheNGARecord^.NGAArtFileName )};
  676.       end;
  677.     end;
  678.     Counter_1 := 0;
  679.     Finished := False;
  680.     while Not Finished do
  681.     begin
  682.       TheNGARecord := PNewsGroupArticleRecord( WorkingList.Items[ Counter_1 ] );
  683.       if ( TheNGARecord^.NGAPosted or TheNGARecord^.NGARead ) then
  684.       begin
  685.         WorkingList.Delete( Counter_1 );
  686.       end
  687.       else Counter_1 := Counter_1 + 1;
  688.       if Counter_1 > WorkingList.Count - 1 then Finished := true;
  689.     end;
  690.   end;
  691.   Result := true;
  692. end;
  693.  
  694.  
  695. { This sends FTP progress text to the Inet form }
  696. procedure TNNTPComponent.ShowProgressErrorText( WhatText : String );
  697. begin
  698.   CCInetCCForm.ShowProgressErrorText( WhatText );
  699. end;
  700.  
  701. { This is a core function! It performs an FTP command and if no timeout }
  702. { return a preliminary ok.                                              }
  703. function TNNTPComponent.PerformNNTPCommand(
  704.                  TheCommand        : string;
  705.            const TheArguments      : array of const ) : Integer;
  706. var TheBuffer : string; { Text buffer }
  707. begin
  708.   { If command in progress send back -1 error }
  709.   if NNTPCommandInProgress then
  710.   begin
  711.     Result := -1;
  712.     exit;
  713.   end;
  714.   { Set status variable }
  715.   NNTPCommandInProgress := True;
  716.   { Set global error code }
  717.   GlobalErrorCode := 0;
  718.   { Format output string }
  719.   TheBuffer := Format( TheCommand , TheArguments );
  720.   { Preset failure code }
  721.   Result := TCPIP_STATUS_FATAL_ERROR;
  722.   { If invalid socket or no connection abort }
  723.   if ( Socket1.TheSocket = INVALID_SOCKET ) or not Connection_Established then
  724.    exit;
  725.   { Send the buffer plus EOL chars }
  726.   Socket1.StringData := TheBuffer + #13#10;
  727.   { if abort due to timeout or other error exit }
  728.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  729.   { Otherwise return preliminary code }
  730.   Result := TCPIP_STATUS_PRELIMINARY;
  731. end;
  732.  
  733. { This is a core function! It performs an FTP command and if no timeout }
  734. { return a preliminary ok.                                              }
  735. function TNNTPComponent.PerformBlindNNTPCommand( TheCommand : string ) : Integer;
  736. var TheBuffer : string; { Text buffer }
  737. begin
  738.   { If command in progress send back -1 error }
  739.   if NNTPCommandInProgress then
  740.   begin
  741.     Result := -1;
  742.     exit;
  743.   end;
  744.   { Set status variable }
  745.   NNTPCommandInProgress := True;
  746.   { Set global error code }
  747.   GlobalErrorCode := 0;
  748.   { Format output string }
  749.   TheBuffer := TheCommand;
  750.   { Preset failure code }
  751.   Result := TCPIP_STATUS_FATAL_ERROR;
  752.   { If invalid socket or no connection abort }
  753.   if ( Socket1.TheSocket = INVALID_SOCKET ) or not Connection_Established then
  754.    exit;
  755.   { Send the buffer plus EOL chars }
  756.   Socket1.StringData := TheBuffer + #13#10;
  757.   { if abort due to timeout or other error exit }
  758.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  759.   { Otherwise return preliminary code }
  760.   Result := TCPIP_STATUS_PRELIMINARY;
  761. end;
  762.  
  763. { This function gets up to 255 chars of data plus a return code from FTP serv }
  764. function TNNTPComponent.GetNNTPServerResponse(
  765.           var ResponseString : String ) : integer;
  766. var
  767.   { Buffer string for response line }
  768.   TheBuffer     : string;
  769.   { Pointer to the response string }
  770.   BufferPointer : array[0..255] of char absolute TheBuffer;
  771.   { Character to check for response code }
  772.   ResponseChar   : char;
  773.   { Pointers into returned string }
  774.   TheIndex ,
  775.   TheLength     : integer;
  776.   { Control variable }
  777.   LeftoversInPan ,
  778.   Finished      : Boolean;
  779. begin
  780.   { Preset fatal error }
  781.   Result := TCPIP_STATUS_FATAL_ERROR;
  782.   { Start loop control }
  783.   LeftoversInPan := false;
  784.   Finished := false;
  785.   repeat
  786.     { Do a peek }
  787.     TheBuffer := Socket1.PeekData;
  788.     { If timeout or other error exit }
  789.     if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  790.     { Find end of line character }
  791.     TheIndex := Pos( #10 , TheBuffer );
  792.     if TheIndex = 0 then
  793.     begin
  794.       TheIndex := Pos( #13 , TheBuffer );
  795.       if TheIndex = 0 then
  796.       begin
  797.         TheIndex := Pos( #0 , TheBuffer );
  798.         if TheIndex = 0 then
  799.         begin
  800.           TheIndex := Length( TheBuffer );
  801.           LeftoversInPan := True;
  802.           LeftoverText := LeftoverText + TheBuffer;
  803.           LeftoversOnTable := false;
  804.         end;
  805.       end;
  806.     end;
  807.     { If an end of line then process the line }
  808.     if TheIndex > 0 then
  809.     begin
  810.       { Get length of string }
  811.       TheLength := TheIndex;
  812.       { Receive actual data }
  813.       Socket1.CCSockReceive( Socket1.TheSocket   ,
  814.                              @BufferPointer[ 1 ] ,
  815.                              TheLength              );
  816.       { Abort if timeout or error }
  817.       if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  818.       { Put in the length byte }
  819.       BufferPointer[ 0 ] := Chr( TheLength );
  820.       if LeftOversOnTable then
  821.       begin
  822.         LeftOversOnTable := false;
  823.         ResponseString := LeftoverText + TheBuffer;
  824.         TheBuffer := ResponseString;
  825.         LeftoverText := '';
  826.       end;
  827.       if LeftoversInPan then
  828.       begin
  829.         LeftoversInPan := false;
  830.         LeftoversOnTable := true;
  831.       end;
  832.       { Get first number character }
  833.       ResponseChar := TheBuffer[ 1 ];
  834.       { Get the value of the number from 1 to 5 }
  835.       if (( ResponseChar >= '1' ) and ( ResponseChar <= '5' )) then
  836.       begin
  837.         Finished := true;
  838.         Result := Ord( ResponseChar ) - 48;
  839.       end;
  840.     end
  841.     else
  842.     begin
  843.     end;
  844.   until ( Finished and ( not LeftoversOnTable ));
  845.   { Return buffer as response string }
  846.   ResponseString := TheBuffer;
  847. end;
  848.  
  849. { Boilerplate error routine }
  850. procedure TNNTPComponent.NNTPSocketsErrorOccurred( Sender     : TObject;
  851.                                                  ErrorCode  : Integer;
  852.                                                  TheMessage : String   );
  853. begin
  854.   CCInetCCForm.SocketsErrorOccurred( Sender,ErrorCode,TheMessage );
  855. end;
  856.  
  857. { This is the FTP components initial connection routine }
  858. function TNNTPComponent.EstablishConnection(
  859.           PCRPointer : PConnectionsRecord ) : Boolean;
  860. var TheReturnString : String;  { Internal string holder }
  861.     TheResult       : Integer; { Internal int holder    }
  862. begin
  863.   { Set default FTP Port value }
  864.   Socket1.PortName := '119';
  865.   { Get the ip address from the record }
  866.   Socket1.IPAddressName := PCRPointer^.CIPAddress;
  867.   { Set blocking mode }
  868.   Socket1.AsynchMode := False;
  869.   { Clear condition variables }
  870.   GlobalErrorCode := 0;
  871.   GlobalAbortedFlag := false;
  872.   { Actually attempt to connect }
  873.   Socket1.CCSockConnect;
  874.   { Check if connected }
  875.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 ) or
  876.       ( Socket1.TheSocket = INVALID_SOCKET )) then
  877.   begin { Didn't connect; signal error and abort }
  878.     { Do clever C formatting trick }
  879.     TheReturnString :=
  880.      DoCStyleFormat( 'NNTP Host %s Connection Failed!' ,
  881.       [ PCRPointer^.CIPAddress ] );
  882.     { Put result in progress and status line }
  883.     AddProgressText( TheReturnString );
  884.     ShowProgressErrorText( TheReturnString );
  885.     { Signal error }
  886.     Result := False;
  887.     { leave }
  888.     exit;
  889.   end
  890.   else
  891.   begin
  892.     Connection_Established := true;
  893.     { Signal successful connection }
  894.     TheReturnString := DoCStyleFormat(
  895.       'Connected on Local port: %s with IP: %s',
  896.       [ Socket1.GetSocketPort( Socket1.TheSocket ),
  897.         Socket1.GetSocketIPAddress( Socket1.TheSocket )]);
  898.     { Put result in progress and status line }
  899.     CCINetCCForm.AddProgressText( TheReturnString );
  900.     CCINetCCForm.ShowProgressText( TheReturnString );
  901.     TheReturnString := DoCStyleFormat(
  902.      'Connected to Remote port: %s with IP: %s',
  903.       [ Socket1.GetSocketPeerPort( Socket1.TheSocket ),
  904.         Socket1.GetSocketPeerIPAddress( Socket1.TheSocket )]);
  905.     { Put result in progress and status line }
  906.     CCINetCCForm.AddProgressText( TheReturnString );
  907.     CCINetCCForm.ShowProgressText( TheReturnString );
  908.     TheReturnString := DoCStyleFormat( 'Successfully connected to %s',
  909.      [ Socket1.IPAddressName ]);
  910.     { Put result in progress and status line }
  911.     CCINetCCForm.AddProgressText( TheReturnString );
  912.     CCINetCCForm.ShowProgressText( TheReturnString );
  913.     repeat
  914.       TheResult := GetNNTPServerResponse( TheReturnString );
  915.       { Put result in progress and status line }
  916.       AddProgressText( TheReturnString );
  917.       ShowProgressText( TheReturnString );
  918.     until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  919.     if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  920.     begin
  921.       { Do clever C formatting trick }
  922.       TheReturnString :=
  923.        DoCStyleFormat( 'NNTP Host %s Connection Failed!' ,
  924.         [ PCRPointer^.CIPAddress ] );
  925.       { Put result in progress and status line }
  926.       AddProgressText( TheReturnString );
  927.       ShowProgressErrorText( TheReturnString );
  928.       { Signal error }
  929.       Result := False;
  930.       { leave }
  931.       exit;
  932.     end
  933.     else Result := true; { Signal no problem }
  934.   end;
  935. end;
  936.  
  937. { This is the FTP component constructor; it creates 2 sockets }
  938. constructor TNNTPComponent.Create( AOwner : TComponent );
  939. begin
  940.   { do inherited create }
  941.   inherited Create( AOwner );
  942.   { Create socket, put in their parent, and error procs }
  943.   Socket1 := TCCSocket.Create( Self );
  944.   Socket1.Parent := Self;
  945.   Socket1.OnErrorOccurred := NNTPSocketsErrorOccurred;
  946.   { Set up booleans }
  947.   Connection_Established := false;
  948.   NNTPCommandInProgress := false;
  949. end;
  950.  
  951. { This is the FTP component destructor; it frees 2 sockets }
  952. destructor TNNTPComponent.Destroy;
  953. begin
  954.   { Free the socket }
  955.   Socket1.Free;
  956.   { and call inherited }
  957.   inherited Destroy;
  958. end;
  959.  
  960. procedure TNNTPComponent.UpdateGauge( BytesFinished , TotalToHandle : longint );
  961. begin
  962.   CCInetCCForm.UpdateGauge( BytesFinished , TotalToHandle );
  963. end;
  964.  
  965. { This sends FTP progress text to the Inet form }
  966. procedure TNNTPComponent.AddProgressText( WhatText : String );
  967. begin
  968.   CCInetCCForm.AddProgressText( WhatText );
  969. end;
  970.  
  971. { This sends FTP progress text to the Inet form }
  972. procedure TNNTPComponent.ShowProgressText( WhatText : String );
  973. begin
  974.   CCInetCCForm.ShowProgressText( WhatText );
  975. end;
  976.  
  977. { This is the FTP components QUIT routine }
  978. function TNNTPComponent.Disconnect : Boolean;
  979. var TheReturnString : String;  { Internal string holder }
  980.     TheResult       : Integer; { Internal int holder    }
  981. begin
  982.   TheReturnString :=
  983.    DoCStyleFormat( 'QUIT' ,
  984.     [ nil ] );
  985.   { Put result in progress and status line }
  986.   AddProgressText( TheReturnString );
  987.   ShowProgressText( TheReturnString );
  988.   { Begin login sequence with user name }
  989.   PerformNNTPCommand( 'QUIT', [ nil ] );
  990.   repeat
  991.     TheResult := GetNNTPServerResponse( TheReturnString );
  992.     { Put result in progress and status line }
  993.     AddProgressText( TheReturnString );
  994.     ShowProgressText( TheReturnString );
  995.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  996.   NNTPCommandInProgress := false;
  997.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  998.   begin
  999.     { Do clever C formatting trick }
  1000.     TheReturnString :=
  1001.      DoCStyleFormat( 'NNTP Host Connection Failed!' ,
  1002.       [ nil ] );
  1003.     { Put result in progress and status line }
  1004.     AddProgressText( TheReturnString );
  1005.     ShowProgressErrorText( TheReturnString );
  1006.     { Signal error }
  1007.     Result := False;
  1008.     { leave }
  1009.     exit;
  1010.   end
  1011.   else Result := true; { Signal no problem }
  1012. end;
  1013.  
  1014. { This is a clever c-style formatting trick }
  1015. function TNNTPComponent.DoCStyleFormat(
  1016.                 TheText      : string;
  1017.           const TheArguments : array of const ) : String;
  1018. begin
  1019.   Result := Format( TheText , TheArguments ) + #13#10;
  1020. end;
  1021.  
  1022.  
  1023. end.
  1024.